import disassemblyParams from './disassemblyParams'
import { appendParams } from './optionParams'
import uploadFile from './uploadFile'

interface header { [key : string] : any }
interface api { [key : string] : any }[]
interface other {
	header ?: header,
	showLoading ?: boolean,
	catch ?: boolean,
	addToUrl ?: boolean
}

class requestClass {
	[key : string] : any;
	configuration = {
		/* 基础域名 */
		baseUrl: <string>'',
		/* 请求头 */
		header: <header>{},
		/* api列表 */
		api: <api>{},
		/* 收否显示Loading动画 */
		showLoading: <boolean>true,
		/* 报错后是否执行cache */
		catch: <boolean>true,
		/* 是否将参数强制追加到地址栏 */
		addToUrl: <boolean>false,
		/* 原生参数 */
		nativeParams: <{ [key : string] : any }>{},
		/* ProxyHandler */
		ProxyHandler: <any>{
			monitor: null,
			// 捕获属性设置操作
			set(target : any, property : any, value : any) {
				const _this = this;
				if (Array.isArray(value))
					target[property] = new Proxy([], {
						set(_target : any, _property : any, _value : any) {
							_target[_property] = _value;
							if (_this.monitor) _this.monitor(target)
							return true;
						}
					})
				else
					target[property] = value;
				if (_this.monitor) _this.monitor(target)
				return true;
			},
			deleteProperty(target : any, property : any) {
				if (property in target) {
					delete target[property];
					if (this.monitor) this.monitor(target)
					return true;

				}
				return false;
			},
		}
	}
	/* 队列 */
	cacheQueue : any = [];
	private _beforeRequest : any = () => { }
	private _afterRequest : any = () => { }
	constructor(options : {
		header ?: header,
		api ?: api,
		baseUrl ?: string,
		showLoading ?: boolean,
		catch ?: boolean,
		addToUrl ?: boolean,
		nativeParams ?: { [key : string] : any }
	} = {}) {
		this.queue = {}
		// 存储 队列 queue proxy 对象到实例属性
		this.proxy = new Proxy(this.queue, this.configuration.ProxyHandler)
		this.cacheQueue = []
		options.header && (this.configuration.header = options.header);
		options.api && (this.configuration.api = options.api);
		options.baseUrl && (this.configuration.baseUrl = options.baseUrl);
		(options.showLoading === true || options.showLoading === false) && (this.configuration.showLoading = options.showLoading);
		(options.catch === true || options.catch === false) && (this.configuration.catch = options.catch);
		(options.addToUrl === true || options.addToUrl == false) && (this.configuration.addToUrl = options.addToUrl);
		options.nativeParams && (this.configuration.nativeParams = options.nativeParams);
		this.init();
	}
	/* 初始化 */
	private init() {
		const api = this.configuration.api
		if (api) {
			if (typeof api === 'object' && !Array.isArray(api)) {
				Object.keys(api).forEach(key => {
					if (!this[key]) {
						this[key] = this.createRequest(api[key], key)
					}
				})
			} else {
				throw 'api is not object'
			}
		}
	}
	/* 请求参数合并 */
	private MergeParameters(url : string, method : string, requestParams : any, other : other) {
		if (!(other.showLoading === true || other.showLoading === false)) {
			other['showLoading'] = this.configuration.showLoading
		}
		if (!(other.catch === true || other.catch === false)) {
			other['catch'] = this.configuration.catch
		}
		if (!(other.addToUrl === true || other.addToUrl === false)) {
			other['addToUrl'] = this.configuration.addToUrl
		}
		/* 处理请求参数强制塞入地址栏URL */
		if (other.addToUrl && method != 'GET') {
			url = appendParams(url, requestParams)
		}
		return {
			url,
			method,
			requestParams,
			baseUrl: this.configuration.baseUrl,
			header: { ...this.configuration.header, ...(other?.header || {}) },
			other,
		}
	}
	/* 创建单个请求 */
	private createRequest(url : string, key : string) {
		const _this = this
		const fun = function (...args : any[]) {
			const _arguments = args
			/* 检测是否从缓存队列中执行 */
			let isRunCacheQueue : any = null
			const isRunCacheQueueIndex = (_arguments as any).findIndex((i : any) => i.isRunCacheQueue === true)
			if (isRunCacheQueueIndex != -1) {
				isRunCacheQueue = _arguments[isRunCacheQueueIndex]
				_arguments.splice(isRunCacheQueueIndex, 1)
			}
			// 解析传递参数
			const { requestParams, method, other } = disassemblyParams(arguments)

			// 合并参数
			const options = _this.MergeParameters(url, method, requestParams, other);

			if (other.uploadFile) {
				uploadFile(options)
			}

			/* loading */
			options.other.showLoading && uni.showLoading({
				mask: true,
				title: '加载中'
			});
			/* loading */

			const PromiseFun = new Promise(async (resolve, reject) => {
				if (isRunCacheQueue) {
					resolve = isRunCacheQueue.resolve
					reject = isRunCacheQueue.reject
				}

				/* 提前注册中断事件 */
				let abortStatus : boolean = false
				let requestTask : any = {
					abort: () => {
						abortStatus = true
					}
				};
				if (!_this.proxy[key])
					_this.proxy[key] = new Array()
				const index = _this.proxy[key].push(requestTask)

				// 执行 请求前前拦截器
				const params = (await _this._beforeRequest(options)) || options

				const requestComplete = async (res : any) => {
					/* loading */
					options.other.showLoading && uni.hideLoading()
					/* loading */
					try {
						const err = await (_this._afterRequest(res, { resolve, reject }, { _arguments, fun, resolve, reject })) || res
						if (res.statusCode == 200) {
							resolve(res)
						} else {
							options.other.catch ? reject(res) : resolve(res)
						}
					} catch (err) {
						if (!(err as any).CacheQueue)
							options.other.catch ? reject(err) : resolve(err)
					}
					/* 清除队列 */
					_this.proxy[key].splice(index - 1, 1);
					if (_this.proxy[key].length == 0)
						delete _this.proxy[key]
				}

				/* 请求前中断 */
				if (abortStatus) return requestComplete({ errMsg: 'request:fail abort' })

				/* 发起请求 */
				const uniRequestParams = {
					url: `${params.baseUrl}${params.url}`,
					method: params.method,
					data: params.requestParams,
					header: params.header,
					async complete(res : any) {
						requestComplete(res)
					}
				}
				_this.proxy[key][index - 1] = uni.request({ ...uniRequestParams, ..._this.configuration.nativeParams, ...(other.nativeParams || {}) });

				/* 请求前中断 */
				if (abortStatus) _this.proxy[key][index - 1].abort()
			})
			return PromiseFun
		}
		return fun
	}
	/* 设置全局请求头 */
	setGlobalHeader(fun : any) {
		const newHeader = fun(this.configuration.header);
		newHeader && (this.configuration.header = newHeader);
	}
	/* 将任务添加到缓存队列 */
	addCacheQueue(task : any) {
		this.cacheQueue.push(task)
	}
	/* 执行缓存队列任务 */
	runCacheQueue() {
		this.configuration.ProxyHandler.monitor = (target : any) => {
			if (Object.keys(target).length == 0) {
				this.configuration.ProxyHandler.monitor = null
				this.cacheQueue.forEach((item : any) => {
					item.fun(...item._arguments, { isRunCacheQueue: true, resolve: item.resolve, reject: item.reject })
				})
			}
		}
	}
	/* 拦截器-请求前 */
	beforeRequest(fun : any) {
		this._beforeRequest = fun
	}
	/* 拦截器-请求后 */
	afterRequest(fun : any) {
		this._afterRequest = fun
	}
	/* 中断请求 */
	abort(key ?: string) {
		if (key) {
			/* 中断某个请求 */
			if (this.proxy[key]) {
				this.proxy[key].forEach((task : any) => {
					task.abort()
				})
			}
		} else {
			/* 中断所有请求 */
			Object.keys(this.proxy).forEach(ObjectKey => {
				this.proxy[ObjectKey].forEach((task : any) => {
					task.abort()
				})
			})
		}
	}
}

export default requestClass